package pars;

import java.util.*;
import fun.grammar.Grammar;
import fun.parser.earley.EarleyParser;
import fun.parser.earley.EarleyParser.PostMortem;
import fun.parser.earley.EarleyState;

import lex.Token;


public class OurParser { 

	/*Program grammar*/
	String GRAMMAR =
			"S -> CLASSDECL*\n" +
			"CLASSDECL* -> CLASSDECL CLASSDECL* | EPSILON\n" +
			"CLASSDECL -> class CLASS_ID EXTEND_CLASSq { FIELD_OR_METHOD* }\n" +
			"EXTEND_CLASSq -> extends CLASS_ID | EPSILON\n" +
			
			"FIELD_OR_METHOD* -> FIELD_OR_METHOD FIELD_OR_METHOD* | EPSILON\n" +
			"FIELD_OR_METHOD -> FIELD | METHOD\n" +
			"FIELD -> FIELD_TYPE ID COMMA_ID* ;\n" +
			
			"FIELD_TYPE -> TYPE | FIELD_TYPE [ ]\n" +				
			"TYPE -> int | boolean | string | CLASS_ID\n" +
			
			"COMMA_ID* -> COMMA_ID COMMA_ID* | EPSILON\n" +
			"COMMA_ID -> , ID\n" +
			
			"METHOD -> TYPE_OR_VOID ID ( FORMALSq ) { STMT* } | static TYPE_OR_VOID ID ( FORMALSq ) { STMT* }\n" +
			"TYPE_OR_VOID -> FIELD_TYPE | void\n" +
		
			"FORMALSq -> FORMAL COMMAFORMAL* | EPSILON\n" + 
			"COMMAFORMAL* -> COMMAFORMAL COMMAFORMAL* | EPSILON\n" +
			"COMMAFORMAL -> , FORMAL\n" +
			"FORMAL -> FIELD_TYPE ID\n" +
		
			"EPSILON ->  \n" + 
			
			/*Statement logic*/
			"STMT* -> STMT STMT* | EPSILON\n" +				
			"STMT -> IF_OR_SIMPLE_STMT \n" +
			"SIMPLE_STMT -> LOCATION = EXPR ; | CALL ; | return EXPR ; | return ; "
					+ "| while ( EXPR ) STMT | break ; | continue ; | { STMT* } "
					+ "| FIELD_TYPE ID ; | FIELD_TYPE ID = EXPR ;\n" +		
			
			/*if logic*/
			"IF_OR_SIMPLE_STMT -> if ( EXPR ) IF_OR_SIMPLE_STMT | IF-ELSE_OR_SIMPLE_STMT\n" +
			"IF-ELSE_OR_SIMPLE_STMT -> if ( EXPR ) IF-ELSE_OR_SIMPLE_STMT else IF_OR_SIMPLE_STMT | SIMPLE_STMT\n" +

			/*Expression logic*/
			"EXPR ->  LOGIC_OR\n" +			
			
			/* Operator precedence flow - Defines operator precedence and associativity according to IC spec. */
			"LOGIC_OR -> LOGIC_OR || LOGIC_AND | LOGIC_AND\n" +
			"LOGIC_AND -> LOGIC_AND && EQ_COMPARISON | EQ_COMPARISON\n" +
			"EQ_COMPARISON -> RELOP == RELOP | RELOP != RELOP | RELOP\n" +
			"RELOP -> PLUS_MINUS < PLUS_MINUS | PLUS_MINUS <= PLUS_MINUS | PLUS_MINUS > PLUS_MINUS | PLUS_MINUS >= PLUS_MINUS | PLUS_MINUS\n" +
			"PLUS_MINUS -> PLUS_MINUS + MULT_DIV_REM | PLUS_MINUS - MULT_DIV_REM | MULT_DIV_REM\n" +
			"MULT_DIV_REM -> MULT_DIV_REM * UNOP | MULT_DIV_REM / UNOP | MULT_DIV_REM % UNOP | UNOP\n" +
			"UNOP -> - DOT_OR_PARA | ! DOT_OR_PARA | DOT_OR_PARA | NEW_TYPE\n" +   
			"NEW_TYPE -> new FIELD_TYPE [ EXPR ] | new FIELD_TYPE [ EXPR ] . length\n" +  /*change*/
			"DOT_OR_PARA -> DOT_OR_PARA . length | DOT_OR_PARA . SIMPLECALL | DOT_OR_PARA . ID | DOT_OR_PARA [ EXPR ] | CLASS_ID . SIMPLECALL | ( EXPR ) | SIMPLE_EXPR\n" +
			"SIMPLE_EXPR -> ID | SIMPLECALL | this | new CLASS_ID ( ) | LITERAL\n" +
			
			"SIMPLECALL -> ID ( ) | ID ( EXPR COMMA_EXPR* )\n" +  
			"COMMA_EXPR* -> COMMA_EXPR COMMA_EXPR* | EPSILON\n" +
			"COMMA_EXPR -> , EXPR \n" +
			
			"LOCATION -> ID | EXPR . ID | EXPR [ EXPR ]\n" +
									
			"CALL -> CLASS_ID . SIMPLECALL | SIMPLECALL | EXPR . SIMPLECALL\n" +
			
			"LITERAL -> INTEGER | STRING | true | false | null\n"; 

	/*Library grammar*/	
	String LIB_GRAMMAR = 
			"S -> class CLASS_ID { LIBMETHOD* }\n" +
			"LIBMETHOD* -> LIBMETHOD LIBMETHOD* | EPSILON\n" +
			"LIBMETHOD -> static RETURN_VALUE ID ( FORMALSq ) ;\n" +
			
			"RETURN_VALUE -> FIELD_TYPE | void\n" +   
			
			"FIELD_TYPE -> TYPE | FIELD_TYPE [ ]\n" +				
			"TYPE -> int | boolean | string | CLASS_ID\n" +
			
			"FORMALSq -> FORMAL COMMAFORMAL* | EPSILON\n" + 
			"COMMAFORMAL* -> COMMAFORMAL COMMAFORMAL* | EPSILON\n" +
			"COMMAFORMAL -> , FORMAL\n" +
			"FORMAL -> FIELD_TYPE ID\n" +
		
			"EPSILON ->  \n";
			
	Grammar programGrammar;
	
	Grammar libGrammar;
	
	
	public OurParser()
	{
		programGrammar = new Grammar(GRAMMAR);
		libGrammar = new Grammar(LIB_GRAMMAR);
	}
	
	
	/** Parses the token list and returns a fun.parser.Tree instance.
	 * In case of successful parsing returns fun.parser.Tree.
	 * In case of a parsing error, prints error message and returns null
	 * */
	fun.parser.Tree parse(Iterable<Token> tokens, Grammar grammar)
	{
		EarleyParser e = new EarleyParser(tokens, grammar);
		
		List<EarleyState> pts = e.getCompletedParses();		
		
		 
		
		//In case of 0 or more than 1 parse trees, deal with problem and exit program
		if (pts.size() != 1){
			
			//in case of error during parsing execution, print suitable error message
			if(pts.size() == 0){
				promptError(e);
				return null;
			}
			
			//Else, more than 1 tree is available. QAQA - should we return null instead?
			System.out.println("Parse error: More than 1 tree is available");
			return null;
		}
		
		/*QAQA*/
		fun.parser.Tree qaTreeHugger = pts.get(0).parseTree();
        System.out.println("\n***** tree : \n" + qaTreeHugger.prettyPrint());
		
		return pts.get(0).parseTree();
	}
	
	
	/** 
	 * Prompts... error!
	 * Input:  EarleyParser e
	 * Prints error to standard output.
	 * */
	private void promptError(EarleyParser e){


		//Need to report what diagnoseError returns
		PostMortem pm = e.diagnoseError();		
		
		//Get last accepted Token
		Token errToken = (Token)pm.token;
		
		
		//Create expectedExpr string here
		String expectedExpr = new String();
		Iterator<String> expectedExpressionsIterator = pm.expecting.iterator();
		if(expectedExpressionsIterator.hasNext()){
			expectedExpr += "'" + expectedExpressionsIterator.next() + "'";
			
			while(expectedExpressionsIterator.hasNext()){
				expectedExpr += " or '" + expectedExpressionsIterator.next() + "'";
			}
		}
		
		//Check Token's validity - if not an instacne of Token, print special error and exit.
		if(!(errToken instanceof Token)){
			System.out.println("at end of input : syntax error; expected " + expectedExpr); 
			return;
		};
		
		//Print error
		System.out.println(errToken.line + ":" + errToken.start + 
									" : syntax error; expected " + expectedExpr +
									", but found " + "'" + errToken.tag + "'\n");	
		
	}
	
	
	/**
	 * Parses the token list and returns a fun.parser.Tree instance.
	 * Input: lsit of tokens, proccessLibrary = true if parsing library grammar, false otherwise
	 * In case of successful parsing returns fun.parser.Tree.
	 * In case of a parsing error, prints error message and returns null
	 */
	public fun.parser.Tree process(Iterable<Token> tokens, boolean proccessLibrary)
	{
		if(proccessLibrary==true){
			return parse(tokens, libGrammar);
		}else{
			return parse(tokens, programGrammar);
		}
		
		//Pay attention: in case of parsing failure, parse returns 
		
		//return constructAst(parse(tokens));
	}
	
	

}